package guzb.cnblogs.security.industrydemo.auth.restful;

import guzb.cnblogs.security.industrydemo.auth.MyUserDetails;
import guzb.cnblogs.security.industrydemo.auth.MyUserDetailsService;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;

import java.util.UUID;

/**
 * 自定义的认证Provider，最终的认证逻辑就在这里执行
 */
public class MyUsernamePasswordAuthenticationProvider implements AuthenticationProvider {

    private MyUserDetailsService userDetailsService;

    public MyUsernamePasswordAuthenticationProvider(MyUserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        MyUsernamePasswordAuthenticationToken myUsernamePasswordAuthenticationToken = (MyUsernamePasswordAuthenticationToken) authentication;
        MyUserDetails userDetails = retrieveUser(myUsernamePasswordAuthenticationToken.getUsername());
        String expectPassword = userDetails.getPassword();
        String actualPassword = myUsernamePasswordAuthenticationToken.getCredentials().toString();
        if (!expectPassword.equals(actualPassword)) {
            throw new BadCredentialsException("密码校验失败，校验账号：" + authentication.getPrincipal());
        }
        return createSuccessAuthentication(userDetails);
    }

    private MyUserDetails retrieveUser(String username) throws AuthenticationException {
        try {
            return (MyUserDetails) userDetailsService.loadUserByUsername(username);
        } catch (Exception ex) {
            throw new InternalAuthenticationServiceException(ex.getMessage(), ex);
        }
    }

    private Authentication createSuccessAuthentication(MyUserDetails userDetails) {
        // 1. 生成Token，这里就使用UUID随机生成
        // 正式环境，也可以使用 jwt 规范，将用户的一些基本信息聚合成一个 token 串
        String token = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
        return new MyUsernamePasswordAuthenticationToken(token, userDetails);
    }

    // 只有当认证参数对象的类型精确地等于 MyUsernamePasswordAuthenticationToken.class 时才进行认证
    @Override
    public boolean supports(Class<?> authenticationClass) {
        return authenticationClass == MyUsernamePasswordAuthenticationToken.class;
    }
}
